﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using OpusSuite.Models.Model.Shared;
using OpusSuite.Models.Model.WebAdmin;
using OpusSuite.Models.Model.Ref;
using OpusSuite.Models.Lib;
using OpusSuite.Models.EFModel;
using OpusSuite.Models.Providers;
using OpusSuite.Utility;
using System.Data.Entity;

namespace OpusSuite.Models
{
    public class OpusSuiteModel
    {
        readonly IOpusSuiteProvider _provider;
        readonly EfModelContext _context;

        public OpusSuiteModel(Enums.ServerTypes serverType, string connectionString)
        {
            switch (serverType)
            {
                case Enums.ServerTypes.SqlServer:
                    _provider = new SqlOpusSuiteProvider(connectionString);
                    break;

                case Enums.ServerTypes.MySqlServer:
                    _provider = new MySqlOpusSuiteProvider(connectionString);
                    break;

                default:
                    throw new ModelException("Cannot get provider", null);
            }

            _context = new EfModelContext(_provider.Connection);
        }

        public EfModelContext Context
        {
            get
            {
                return _context;
            }
        }

        public bool SignInUser(string userName, string password, string databaseName, string serverName, string ipAddress)
        {
            try
            {
                var user = _context.Users.FirstOrDefault(x => x.UserName == userName);

                if (user != null)
                {
                    UserSession session = user.Session;
                    // update session with details
                    session.IsLoggedIn = true;
                    session.LastLoggedInDate = session.LoggedInDate;
                    session.LoggedInIP = ipAddress;
                    session.LoggedInDate = DateTime.Now;
                    _context.SaveChanges();
                }
            }
            catch (Exception e)
            {
                throw new ModelException("Cannot find user " + userName + ": ", e);
            }

            return true;
        }

        public bool SignOutUser(string userName)
        {
            try
            {
                var user = _context.Users.Where(x => x.UserName == userName).Include(x => x.Session).FirstOrDefault();

                if (user != null)
                {
                    // Add session update
                    var session = user.Session;
                    // update session with details
                    session.LoggedOutDate = DateTime.Now;
                    session.IsLoggedIn = false;
                    _context.SaveChanges();
                }
            }
            catch (Exception e)
            {
                throw new Exception("Cannot find user " + userName + ". e: " + e.Message);
            }

            return true;
        }

        public void SetupCreateDatabase(string dbUserName, string dbPassword, string dbDatabase, string dbServer, Enums.ServerTypes dbServerType, string adminUserName)
        {
            if (_provider != null)
            {
                HttpCookie cookie = WebUtil.CookieFromLogin(dbUserName, dbPassword, dbDatabase, dbServer, dbServerType);
                HttpContext.Current.Response.Cookies.Add(cookie);

                _provider.SetupCreateDatabase(this, dbUserName, dbPassword, dbDatabase, dbServer, adminUserName);

                // Seed the reference database

                // the model meta data, for upgrade etc and to make sure ef/nh models arent mixed
                _context.ModelMeta.Add(new ModelMeta { MajorVersion = 0, MinorVersion = 0, RevisionVersion = 1 });

                Theme t = new Theme
                              {
                                  Code = "south-street",
                                  Name = "Green",
                                  Description = "Default green theme"
                              };
                _context.Themes.Add(t);

                Language l = new Language
                                 {
                                     Code = "en",
                                     Name = "English",
                                     Description = "English"
                                 };
                _context.Languages.Add(l);

                l = new Language
                        {
                            Code = "es",
                            Name = "España",
                            Description = "español"
                        };
                _context.Languages.Add(l);

                MoneyFormat m = new MoneyFormat
                                    {
                                        Code = "en-gb-1",
                                        Name = "9,999.99",
                                        Description = "British Format"
                                    };
                _context.MoneyFormats.Add(m);

                m = new MoneyFormat
                        {
                            Code = "es-1",
                            Name = "9.999,99",
                            Description = "formato español"
                        };
                _context.MoneyFormats.Add(m);

                DateFormat d = new DateFormat
                                   {
                                       Code = "en-gb-1",
                                       Name = "DD/MM/YYYY",
                                       Description = "British Format"
                                   };
                _context.DateFormats.Add(d);

                d = new DateFormat
                        {
                            Code = "es-1",
                            Name = "DD/MM/YYYY",
                            Description = "formato español"
                        };
                _context.DateFormats.Add(d);

                _context.SaveChanges();

                Country c = new Country
                                {
                                    Code = "en-gb",
                                    Description = "United Kingdom",
                                    Name = "United Kingdom",
                                    DateFormat = _context.DateFormats.FirstOrDefault(x => x.Code == "en-gb-1"),
                                    MoneyFormat = _context.MoneyFormats.FirstOrDefault(x => x.Code == "en-gb-1"),
                                    Language = _context.Languages.FirstOrDefault(x => x.Code == "en")
                                };
                _context.Countries.Add(c);

                c = new Country
                        {
                            Code = "es",
                            Description = "España",
                            Name = "España",
                            DateFormat = _context.DateFormats.FirstOrDefault(x => x.Code == "es-1"),
                            MoneyFormat = _context.MoneyFormats.FirstOrDefault(x => x.Code == "es-1"),
                            Language = _context.Languages.FirstOrDefault(x => x.Code == "es")
                        };
                _context.Countries.Add(c);

                _context.SaveChanges();

                // Default user profile
                UserProfile p = new UserProfile
                                    {
                                        DisplayName = "Administrator",
                                        Country = _context.Countries.FirstOrDefault(x => x.Code == "en-gb"),
                                        Theme = _context.Themes.FirstOrDefault(x => x.Code == "south-street")
                                    };
                p.AddUserSetting(Enums.SettingType.ShowTooltips, true);

                _context.UserProfiles.Add(p);

                User u = new User
                             {
                                 UserName = adminUserName,
                                 Password = CryptoUtil.EncryptStringAES(adminUserName, ConfigUtil.AESKey()),
                                 Profile = p,
                                 Session = new UserSession
                                               {
                                                   IsLoggedIn = false,
                                                   LastLoggedInDate = new DateTime(1900, 1, 1, 1, 1, 1),
                                                   LastLoggedInIP = "",
                                                   LoggedInDate = new DateTime(1900, 1, 1, 1, 1, 1),
                                                   LoggedInIP = "",
                                                   LoggedOutDate = new DateTime(1900, 1, 1, 1, 1, 1)
                                               }
                             };

                _context.Users.Add(u);

                _context.SaveChanges();

                cookie = WebUtil.CookieFromLogin(adminUserName, adminUserName, dbDatabase, dbServer, dbServerType);
                HttpContext.Current.Response.Cookies.Remove("OpusSuiteAuth");
                HttpContext.Current.Response.Cookies.Add(cookie);
            }
            else
            {
                throw new ModelException("Cannot create database, unsupported server type " + dbServerType, null);
            }
        }

        public IList<ApplicationMessage> GetApplicationMessages(bool showToUser)
        {
            try
            {
                var q = from message in _context.ApplicationMessages
                               where message.ShowToUser == showToUser // && message.Type == Enums.ApplicationMessageType.Message
                               select message;

                var messages = q.ToList();

                 //now we have the messages delete the ones that are marked as 'Once'
                 //so they dont appear again.
                var deleteList = new List<ApplicationMessage>();

                foreach (var item in messages.Where(item => item.Lifetime == Enums.ApplicationMessageLifetime.Once).Where(item => item.MarkForDeletion))
                {
                    item.MarkForDeletion = false;
                    item.ShowToUser = false;
                    _context.SaveChanges();
                    deleteList.Add(item);
                }

                foreach (var item in deleteList)
                {
                    messages.Remove(item);
                }

                 //now we have the messages delete the ones that are marked as 'Once'
                 //so they dont appear again.
                foreach (var item in messages.Where(item => item.Lifetime == Enums.ApplicationMessageLifetime.Once))
                {
                    item.MarkForDeletion = true;
                    _context.SaveChanges();
                }

                return messages;
            }
            catch (Exception e)
            {
                throw new ModelException("Cannot get messages from the database: ", e);
            }
        }

        public void AddApplicationMessage(Enums.ApplicationMessageType type, Enums.ApplicationMessageLevel level, string message, string description, User user, Enums.ApplicationMessageLifetime lifetime = Enums.ApplicationMessageLifetime.Once, int seconds = 0)
        {
            try
            {
                ApplicationMessage m = new ApplicationMessage
                {
                    Type = type,
                    Level = level,
                    Message = message,
                    Description = description,
                    MessageDate = DateTime.Now,
                    Lifetime = lifetime,
                    ShowToUser = true
                };
                if (user != null && !string.IsNullOrWhiteSpace(user.UserName))
                    m.MessageUser = user;

                if (lifetime == Enums.ApplicationMessageLifetime.Seconds)
                    m.Timeout = seconds;

                _context.ApplicationMessages.Add(m);
                _context.SaveChanges();
            }
            catch (Exception e)
            {
                throw new ModelException("Cannot add message (" + message + ") to the database: ", e);
            }
        }

        //public void AddValidationMessage(string message, string description)
        //{
        //    try
        //    {
        //        ApplicationMessage m = new ApplicationMessage()
        //        {
        //            Type = Utility.Enums.ApplicationMessageType.Message,
        //            Level = Utility.Enums.ApplicationMessageLevel.Error,
        //            Message = message,
        //            Description = description,
        //            MessageUser = new User(),
        //            Lifetime = Utility.Enums.ApplicationMessageLifetime.Once
        //        };

        //        if (!_context.ApplicationMessages.Contains(m))
        //        {
        //            _context.ApplicationMessages.Add(m);
        //            _context.SaveChanges();
        //        }
        //    }
        //    catch (Exception e)
        //    {
        //        throw new ModelException("Cannot add validation message (" + message + ") to the database: ", e);
        //    }
        //}

        public void AcceptApplicationMessage(int messageId)
        {
            try
            {
                ApplicationMessage message = _context.ApplicationMessages.FirstOrDefault(x => x.Id == messageId);

                // now we have the messages delete the ones that are marked as 'Once'
                // so they dont appear again.
                if (message !=null && message.Id == messageId)
                {
                    message.ShowToUser = false;
                    _context.SaveChanges();
                }
            }
            catch (Exception e)
            {
                throw new ModelException("Cannot accept message: " , e);
            }
        }

        // TODO remove and put code into controller maybe
        public void ChangePassword(User u, string oldPassword, string newPassword)
        {
            if (u.ValidatePassword(oldPassword))
            {
                u.SetPassword(newPassword);
                _context.SaveChanges();
                _provider.ChangePassword(u.UserName, newPassword);
            }
            else
            {
                throw new ModelException(string.Format("Validation failed changing password for {0}: {1}", u.UserName, "Old password is not valid."), null);
            }

        }
    }
}
